package com.ebarlas.fractal.util;

public class Complex {   
    
    public static final Complex TRUE = new Complex(1.0, 0.0);
    public static final Complex FALSE = new Complex(0.0, 0.0);
    public static final Complex ZERO = new Complex(0.0, 0.0);
    public static final Complex ONE = new Complex(1.0, 0.0);
    public static final Complex TWO = new Complex(2.0, 0.0);
    public static final Complex THREE = new Complex(3.0, 0.0);
    public static final Complex I = new Complex(0.0, 1.0);
    
    private double re;
    private double im;
    
    public Complex() {
        this(0.0, 0.0);
    }
    
    public Complex(boolean b) {
        this(b ? 1.0 : 0.0, 0.0);
    }    
    
    public Complex(int n) {
        this(n, 0.0);
    }
    
    public Complex(double re) {
        this(re, 0.0);
    }    
    
    public Complex(double re, double im) {
        this.re = re;
        this.im = im;
    }    
    
    public static double abs(double re, double im) {
        double reAbs = Math.abs(re);
        double imAbs = Math.abs(im);

        if (reAbs == 0.0 && imAbs == 0.0) {
            return 0.0;
        } else if (reAbs >= imAbs) {
            double d = im / re;
            return reAbs * Math.sqrt(1.0 + d*d);
        } else {
            double d =  re / im;
            return imAbs * Math.sqrt(1.0 + d*d);
        }
    }
    
    public static Complex abs(Complex a) {
        return new Complex(abs(a.re, a.im));
    }    
    
    public static Complex add(Complex a, Complex b) {
        return new Complex(a.re+b.re, a.im+b.im);
    }
    
    public static Complex and(Complex a, Complex b) {
        return a.re != 0.0 && b.re != 0.0 ? TRUE : FALSE;
    }
    
    public static Complex divide(Complex a, Complex b) {
        double ratio, denom, re, im;
        
        if(Math.abs(b.re) >= Math.abs(b.im)){
            ratio = b.im / b.re;
            denom = b.re + b.im * ratio;
            re = (a.re + a.im * ratio) / denom;
            im = (a.im - a.re * ratio) / denom;
        } else{
            ratio = b.re / b.im;
            denom = b.re * ratio + b.im;
            re = (a.re * ratio + a.im) / denom;
            im =(a.im * ratio - a.re) / denom;
        }
    
        return new Complex(re, im);        
    }
    
    @Override
    public boolean equals(Object obj) {
        return obj != null 
                && obj instanceof Complex 
                && (re == ((Complex)obj).re && im == ((Complex)obj).im);
    }    
    
    public static Complex equals(Complex a, Complex b) {
        return a.re == b.re && a.im == b.im ? TRUE : FALSE;
    }
    
    public static Complex exp(Complex a) {
        double d = Math.exp(a.re);
        return new Complex(d * Math.cos(a.im), d * Math.sin(a.im));         
    }
    
    public boolean getBoolean() {
        return re != 0.0;
    }        
    
    public double getImaginary() {
        return im;
    }
    
    public int getInt() {
        return (int)re;
    }   
    
    public double getReal() {
        return re;
    }   
    
    public static Complex greaterThan(Complex a, Complex b) {
        return a.re > b.re ? TRUE : FALSE;
    }
    
    public static Complex greaterThanEquals(Complex a, Complex b) {
        return a.re >= b.re ? TRUE : FALSE;
    }    
    
    public static Complex If(Complex cond, Complex a, Complex b) {
        return cond.re != 0.0 ? a : b;
    }
    
    public boolean isComplex() {
        return im != 0.0;
    }
    
    public boolean isZero() {
        return re == 0.0 && im == 0.0;
    }
    
    public static Complex lessThan(Complex a, Complex b) {
        return a.re < b.re ? TRUE : FALSE;
    }
    
    public static Complex lessThanEquals(Complex a, Complex b) {
        return a.re <= b.re ? TRUE : FALSE;
    }    
    
    public static Complex log(Complex a) {
        return new Complex(Math.log(abs(a.re, a.im)), Math.atan2(a.im, a.re));
    }
    
    /**
     * z^2 + c
     */
    public static Complex mbrot(Complex c, Complex z) {
        return new Complex(z.re*z.re - z.im*z.im + c.re, 2 * z.re * z.im + c.im);
    }
    
    public static Complex mod(Complex a, Complex b) {
        return new Complex((int)a.re % (int)b.re);
    }
    
    public static Complex multiply(Complex a, Complex b) {
        return new Complex(a.re*b.re - a.im*b.im, a.re*b.im + a.im*b.re);
    }  
    
    public static Complex negate(Complex a) {
        return new Complex(-a.re, -a.im);
    }  
    
    public static Complex not(Complex a) {
        return new Complex(!(a.re != 0.0));
    }
    
    public static Complex notEquals(Complex a, Complex b) {
        return a.re != b.re || a.im != b.im ? TRUE : FALSE;
    }
    
    public static Complex or(Complex a, Complex b) {
        return a.re != 0.0 || b.re != 0.0 ? TRUE : FALSE;
    }
    
    public static Complex pow(Complex base, double exponent) {
        double re = exponent * Math.log(abs(base.re, base.im));
        double im = exponent * Math.atan2(base.im, base.re);
        double scalar = Math.exp(re);
        return new Complex(scalar * Math.cos(im), scalar * Math.sin(im) );
    }
    
    public static Complex pow(double base, Complex exponent) {
        double re = Math.log(Math.abs(base));
        double im = Math.atan2(0.0, base);
        double re2 = (re*exponent.re) - (im*exponent.im);
        double im2 = (re*exponent.im) + (im*exponent.re);
        double scalar = Math.exp(re2);
        return new Complex(scalar * Math.cos(im2), scalar * Math.sin(im2));
    }    
    
    public static Complex pow(Complex base, Complex exponent) {
        if(base.im == 0.0 && exponent.im == 0.0) {
            return new Complex(Math.pow(base.re, exponent.re));
        } else if(base.im == 0.0) {
            return pow(base.re, exponent);
        } else if(exponent.im == 0.0) {
            return pow(base, exponent.re);
        } else {
            double re = Math.log(abs(base.re, base.im));
            double im = Math.atan2(base.im, base.re);
            double re2 = (re*exponent.re) - (im*exponent.im);
            double im2 = (re*exponent.im) + (im*exponent.re);
            double scalar = Math.exp(re2);
            return new Complex(scalar * Math.cos(im2), scalar * Math.sin(im2));
        }
    }
    
    public static Complex square(Complex a) {
        return new Complex(a.re*a.re - a.im*a.im, 2 * a.re * a.im);
    }
    
    public static Complex subtract(Complex a, Complex b) {
        return new Complex(a.re-b.re, a.im-b.im);
    }
    
    public String toString() {
        return re + " + " + im + "i";
    }    
    
}
